---
title: Provider
---

import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import CodeBlock from "@theme/CodeBlock";
import todo from "!!raw-loader!/i18n/ja/docusaurus-plugin-content-docs/current/providers/provider/todo.dart";
import completedTodos from "!!raw-loader!/i18n/ja/docusaurus-plugin-content-docs/current/providers/provider/completed_todos.dart";
import todosConsumer from "!!raw-loader!/i18n/ja/docusaurus-plugin-content-docs/current/providers/provider/todos_consumer.dart";
import unoptimizedPreviousButton from "!!raw-loader!/i18n/ja/docusaurus-plugin-content-docs/current/providers/provider/unoptimized_previous_button.dart";
import optimizedPreviousButton from "!!raw-loader!/i18n/ja/docusaurus-plugin-content-docs/current/providers/provider/optimized_previous_button.dart";
import { trimSnippet } from "../../../../../src/components/CodeSnippet";

`Provider` はプロバイダの中で最もベーシックなプロバイダであり、値を同期的に生成してくれます。

一般的には次のような用途で使われます。

- 計算結果をキャッシュするため。
- 他のプロバイダに値（例えば `Repository` や `HttpClient` のインスタンス）を公開するため。
- テスト実施時やウィジェット構築時に値をオーバーライドするため。
- `select` を使わずにプロバイダやウィジェットの更新の条件を限定するため。

## `Provider` を使って計算結果をキャッシュする

`Provider` は [ref.watch] と組み合わせることで、同期的な処理の結果をキャッシュする強力なツールとなります。

例えば、Todo リストにフィルタを適用するような場合です。
この種のフィルタリングは負荷が若干高くなる可能性があるため、画面が再描画するたびにフィルタが適用されてしまうようなことは避けたいものです。
そこで登場するのが `Provider` です。`Provider` にフィルタリングを任せて、その計算結果をキャッシュしましょう。

Todo リストを管理する、次のような [StateNotifierProvider] があるとします。

<CodeBlock>{trimSnippet(todo)}</CodeBlock>

そして `Provider` を使って Todo リストをフィルタリングし、完了タスクのみを残してリストを公開します。

<CodeBlock>{trimSnippet(completedTodos)}</CodeBlock>

ウィジェットは `completedTodosProvider` を監視することで、完了タスクのみを UI として表示することができます。

<CodeBlock>{trimSnippet(todosConsumer)}</CodeBlock>

これでフィルタリングの計算結果をキャッシュすることができました。

`completedTodosProvider` が公開する完了タスクのリストは、何度値を取得しようが、新たに Todo が追加・削除・更新されるまで再評価されることはありません。

また、Todo リストの内容が変わった際に手動でキャッシュを無効化する必要がない点にお気づきでしょうか。
[ref.watch] があるおかげで、`Provider` は値を再評価するタイミングを自動検知することができるのです。

## `Provider` を使ってプロバイダやウィジェットの更新の条件を限定する

`Provider` が他のプロバイダと異なる点は、[ref.watch] を使うなどして `Provider` の値が再評価された際に、
その値が以前の値と変わらない場合は、値を監視する別のプロバイダもしくはウィジェットに通知しないという点です。
つまり、そのプロバイダもしくはウィジェットが更新されることはないということです。

この特性を活用できる例としては、ページネーションが施されたページの「戻る/次へ」ボタンの有効・無効の切り替えが挙げられます。

![「戻る/次へ」ボタン](https://user-images.githubusercontent.com/134939/47580830-31263a00-d950-11e8-9b61-0eaddab2709e.png)

ここでは「戻る」ボタンにのみフォーカスしてサンプルコードをご紹介したいと思います。
まず、現在のページインデックスを取得し、それが 0 に等しい場合はボタンが無効化するようなウィジェットを準備します。

コードは次のようになります。

<CodeBlock>{trimSnippet(unoptimizedPreviousButton)}</CodeBlock>

しかし、このコードには問題があります。それは現在のページが変わるたびに「戻る」ボタンが更新されてしまうことです。
ここはボタンの有効・無効が切り替わるときにのみ、ボタンが更新されるのが理想的ですよね。

この問題の要因は、ユーザが前のページに戻れるか否かの指標を「戻る」ボタンのウィジェットが直接計算してしまっていることにあります。

よって、この問題を解決するにはロジックをウィジェットから抽出して `Provider` に計算を任せる必要があります。

<CodeBlock>{trimSnippet(optimizedPreviousButton)}</CodeBlock>

このような小さなリファクタリングを行うことで、`PreviousButton`
ウィジェットはページインデックスが変わっても更新されることがなくなりました。

ページインデックスが変わると `canGoToPreviousPageProvider` の値は再評価されますが、
値が直前に公開したものと変わらなければ、`PreviousButton` は更新されません。

これでボタンのパフォーマンスが改善し、さらにロジックとウィジェットを分離して管理することができるようになりました。
これは `Provider` のユニークな特性のおかげによるものです。

[ref.watch]: ../concepts/reading#refwatch-を使ってプロバイダを監視する
[statenotifierprovider]: ./state_notifier_provider
